﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LightPersist.Exceptions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MyUtils;
using TestDomainModel;
using System.Diagnostics;

namespace LightPersistTests
{
	[TestClass]
	public class Exceptional_situations : TripleTestsBase
	{
		[TestMethod]
		public void Should_not_allow_create_cycle()
		{
			TraceToLog();

			var currentChild = child;

			for(int i = 0; i < 20; i++)
			{
				var newChild = Context.Create<FakeControl>();
				currentChild.Children.Add(newChild);
				currentChild = newChild;
			}

			Reconnect();

			child = Context.GetObjectByID<FakeControl>(child.ID);
			currentChild = Context.GetObjectByID<FakeControl>(currentChild.ID);
			currentChild.Children.Add(child);

			Throws<DagException>(() => tr.CommitTransaction());

			Assert.AreEqual("Error", tr.Reflect("_transaction state tostring"));

			tr.RollbackTransaction();

			tr = Context.BeginTransaction();
		}

		[TestMethod]
		public void Should_notify_and_store_valid_values_during_rollback_if_DAG_cycle_catched_inside_the_middle_of_processing()
		{
			int widthNotified = child.Width;
			child.PropertyChanged += (s, e) =>
			{
				if(e.PropertyName == "Width")
				{
					try
					{
						widthNotified = child.Width;
					}
					catch
					{
						//widthNotified = -1;
					}
				}
			};
			var newChild = Context.Create<FakeControl>();
			child.Width = 200;
			child.Children.Add(newChild);
			newChild.Children.Add(child);
			Throws<DagException>(() => tr.CommitTransaction());

			Equal(200, child.Reflect("objectstate get", "Width"));

			tr.RollbackTransaction();

			Context.Reflect("cache clear");

			tr = Context.BeginTransaction();
			child = Context.GetObjectByID<FakeControl>(childID);
			Console.WriteLine("Assert False 200 == " + child.Width);
			False(200 == child.Width);
			Equal(child.Width, widthNotified);
		}

	}

	[TestClass]
	public class Exceptional_situations2 : ContextTestsBase
	{
		[TestMethod]
		public void Should_invalidate_transaction_on_any_invalid_action()
		{
			ChangeTransaction();
			Throws<ArgumentOutOfRangeException>(delegate
			{
				Root.Children.Insert(-1, Context.Create<FakeControl>());
			});
			Throws<TransactionException>(delegate
			{
				tr.CommitTransaction();
			});
			NotThrows(delegate { tr.RollbackTransaction(); });
			NotThrows(delegate { Context.BeginTransaction(); });
		}

		[TestMethod]
		public void Should_throw_on_any_action_after_transaction_is_invalidated()
		{
			ChangeTransaction();
			Throws<ArgumentOutOfRangeException>(delegate
			{
				Root.Children.Insert(-1, Context.Create<FakeControl>());
			});

			Throws<TransactionException>(delegate
			{
				Context.Create<FakeControl>();
			});

			Throws<TransactionException>(delegate
			{
				Root.Children[0].Width = 5;
			});

		}

		[TestMethod]
		public void Should_not_allow_changing_object_during_notify()
		{
			child.PropertyChanged += delegate
			{
				child.Width = 777;
			};
			child.Name = "test";
			var ex = Throws<ObjectIsReadonlyException>(delegate { tr.CommitTransaction(); });
			Assert.IsNotNull(ex);
			Assert.IsNotNull(ex.Message);
			Assert.IsTrue(ex.Message.Contains("notify"));
		}

		[TestMethod]
		public void Should_allow_reading_object_during_notify()
		{
			child.PropertyChanged +=(s,e) =>
			{
				if(e.PropertyName=="IsReadonly")return;
				Assert.AreEqual(777, child.Width);
			};
			child.Width = 777;
			NotThrows(delegate { tr.CommitTransaction(); });
		}

		[TestMethod]
		public void Should_allow_make_invalid_graph_state_inside_transaction_and_check_only_on_commit()
		{

			Assert.Inconclusive();
		}

		[TestMethod]
		public void Should_not_allow_to_create_loop()
		{
			CommitIfNeed();

			Throws<DagException>(() =>
			{
				Context.Execute = delegate
				{
					NotThrows(delegate
					{
						var child2 = Context.Create<FakeControl>();
						child.Content = child2;
						child.Width = 800;
						child2.Content = child;
					});
				};
			});

			Context.BeginTransaction();

			Assert.AreNotEqual(800, child.Width, "transaction must be rolled back");
		}

	}

	[TestClass]
	public class __Initialize_before_insert : TripleTestsBase {

		[TestMethod]
		public void Should_allow_initializing_object_before_insering_into_collection()
		{
			var item = Context.Create<FakeControl>();
			item.Name = "test";
			item.Width = 777;
			Root.Children.Add(item);

			Reconnect();

			Assert.AreEqual("test", Context.GetObjectByID<FakeControl>(item.ID).Name);
			Assert.AreEqual(777, Context.GetObjectByID<FakeControl>(item.ID).Width);
		}

		[TestMethod]
		public void Should_allow_initializing_object_tree_before_insering_into_collection()
		{
			var item = Context.Create<FakeControl>();
			item.Name = "test";
			item.Width = 777;
			item.Children.Add(Context.Create<FakeControl>());
			Root.Children.Add(item);

			Reconnect();

			Assert.AreEqual("test", Context.GetObjectByID<FakeControl>(item.ID).Name);
			Assert.AreEqual(777, Context.GetObjectByID<FakeControl>(item.ID).Width);
		}

	}
}
